package com.fubowen.reader.vm

import android.app.Application
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.Paint
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.LayerDrawable
import android.graphics.drawable.ShapeDrawable
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
import androidx.core.graphics.ColorUtils
import androidx.core.widget.ImageViewCompat
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.fubowen.reader.db.bean.ThemeBean
import com.fubowen.reader.db.bean.ThemeConfigBean
import com.fubowen.reader.db.databaseOfTheme
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch

const val BACKGROUND_COLOR = "BACKGROUND_COLOR"
const val TEXT_COLOR = "TEXT_COLOR"
const val CHECKED_COLOR = "CHECKED_COLOR"
const val UNCHECKED_COLOR = "UNCHECKED_COLOR"
const val PROGRESS_BAR_COLOR = "PROGRESS_BAR_COLOR"
const val COLOR_LIGHTEN = "lighten"
const val COLOR_DEEPEN = "deepen"
const val NO_THEME = "noTheme"
const val COLOR_INVERT = "invert"
const val SAME_COLOR_TO_STROKE = "stroke"
const val STROKE_WIDTH = 4

class ThemeViewModel(application: Application) : AndroidViewModel(application) {

    private var mThemeChangedJob: Job? = null
    private val mThemeFlow = MutableStateFlow(whiteTheme())
    private val mDefaultThemeList = mutableListOf<ThemeWithValueResult>()
    private val mDatabase by databaseOfTheme()
    private val mThemeDao = mDatabase.themeDao()
    val themeFlow get() = mThemeFlow.asStateFlow()

    init {
        checkOrSave()
        observeThemeChanged()
    }

    private fun checkOrSave() {
        mDefaultThemeList.add(whiteTheme())
        mDefaultThemeList.add(greenTheme())
        mDefaultThemeList.add(yellowTheme())
        mDefaultThemeList.add(blueTheme())
        mDefaultThemeList.add(darkTheme())

        val insertThemeList = mutableListOf<ThemeWithValueResult>()
        val updateThemeList = mutableListOf<ThemeWithValueResult>()
        val themeList = mThemeDao.getByName(mDefaultThemeList.map { it.themeBean.name })
        for (item in mDefaultThemeList) {
            val theme = themeList[item.themeBean.name]
            if (theme == null) {
                insertThemeList.add(item)
            } else if (theme.version != item.themeBean.version) {
                item.themeBean.id = theme.id
                updateThemeList.add(item)
            }
        }
        if (insertThemeList.isNotEmpty()) {
            mThemeDao.setTheme(insertThemeList)
        }
        if (updateThemeList.isNotEmpty()) {
            mThemeDao.putTheme(updateThemeList)
        }
    }

    private fun observeThemeChanged() {
        mThemeChangedJob?.cancel()
        mThemeChangedJob = viewModelScope.launch {
            mThemeDao.getUsedFlow().collect { it?.apply { setTheme(this.name) } }
        }
    }

    fun setTheme(name: String) = viewModelScope.launch {
        val theme = mDefaultThemeList.find { it.themeBean.name == name } ?: mThemeDao.getThemeByNameToInherit(name)
        if (theme != null) {
            mThemeFlow.value = theme
        }
    }

    //region 读取当前主题中的属性值
    fun getInt(key: String) = themeFlow.value.themeConfigBeanList[key]?.value?.toIntOrNull()

    fun getString(key: String) = themeFlow.value.themeConfigBeanList[key]?.value

    fun ifExistToCallback(key: String, callback: (String) -> Unit) {
        val value = themeFlow.value.themeConfigBeanList[key]
        if (value != null) {
            callback(value.value)
        }
    }

    fun getBackgroundColor(): Int {
        val value = getString(BACKGROUND_COLOR)
        if (value.isNullOrBlank()) {
            return Color.TRANSPARENT
        }
        return Color.parseColor(value)
    }

    fun getTextColor(): Int {
        val value = getString(TEXT_COLOR)
        if (value.isNullOrBlank()) {
            return Color.BLACK
        }
        return Color.parseColor(value)
    }

    fun getCheckedColor(): Int {
        val value = getString(CHECKED_COLOR)
        if (value.isNullOrBlank()) {
            return Color.GREEN
        }
        return Color.parseColor(value)
    }

    fun getColorOfCheckedState(isChecked: Boolean) = if (isChecked) getCheckedColor() else getUncheckedColor()
    fun getUncheckedColor(): Int {
        val value = getString(UNCHECKED_COLOR)
        if (value.isNullOrBlank()) {
            return Color.GRAY
        }
        return Color.parseColor(value)
    }

    fun getProgressBarColor(): Int {
        val value = getString(PROGRESS_BAR_COLOR)
        if (value.isNullOrBlank()) {
            return changeColorRGB(getBackgroundColor(), -16, -16, -16)
        }
        return Color.parseColor(value)
    }

    //endregion

    //region 将主题应用到 View

    //region setViewTheme
    fun setViewTheme(view: View) {
        setViewThemeImpl(view)
        if (view is ViewGroup) {
            for (i in 0 until view.childCount) {
                val child = view.getChildAt(i)
                setViewTheme(child)
            }
        }
    }
    //endregion

    //region setViewThemeImpl
    private fun setViewThemeImpl(view: View) {
        if (view.isNoTheme()) {
            return
        }
        var bgColor = getBackgroundColor()
        if (view.isLighten()) {
            bgColor = changeColorRGB(bgColor, 31, 31, 31)
        }
        if (view.isSameColorToStroke()) {
            setSameColorToStroke(view, bgColor)
            return
        }
        if (view is SeekBar) {
            setThemeSeekBar(view, bgColor)
            return
        }
        when (val bg = view.background) {
            is ColorDrawable -> view.setBackgroundColor(bgColor)
            is GradientDrawable -> bg.setColor(bgColor)
            else -> view.setBackgroundColor(bgColor)
        }
        val textColor = getTextColor()
        when (view) {
            is TextView -> view.setTextColor(textColor)
            is ImageView -> ImageViewCompat.setImageTintList(view, ColorStateList.valueOf(textColor))
        }
    }
    //endregion

    //region setSameColorToStroke
    private fun setSameColorToStroke(view: View, bgColor: Int) {
        when (val bg = view.background) {
            is ColorDrawable -> {
                if (bgColor == bg.color) {
                    val borderDrawable = GradientDrawable()
                    borderDrawable.setStroke(STROKE_WIDTH, Color.BLACK)
                    borderDrawable.setColor(bgColor)
                    view.background = borderDrawable
                }
            }

            is GradientDrawable -> {
                if (bgColor == bg.color?.defaultColor) {
                    bg.setStroke(STROKE_WIDTH, Color.BLACK)
                }
            }

            is ShapeDrawable -> {
                if (bgColor == bg.paint.color) {
                    bg.paint.color = Color.BLACK;
                    bg.paint.style = Paint.Style.STROKE;
                    bg.paint.strokeWidth = STROKE_WIDTH.toFloat();
                }
            }
        }
    }
    //endregion

    //region SeekBar
    private fun setThemeSeekBar(seekBar: SeekBar, bgColor: Int) {
        val thumb = seekBar.thumb
        if (thumb is LayerDrawable) {
            val count = thumb.numberOfLayers
            for (i in 0 until count) {
                when (val layer = thumb.getDrawable(i)) {
                    is ShapeDrawable -> layer.paint.color = bgColor
                    is GradientDrawable -> layer.setColor(bgColor)
                    is ColorDrawable -> layer.color = bgColor
                }
            }
        }
    }
    //endregion

    //region 白色主题
    private fun whiteTheme() = ThemeWithValueResult(
        themeBean = ThemeBean(name = "白色主题", description = "", isUsed = true),
        themeConfigBeanList = mutableMapOf(
            BACKGROUND_COLOR to ThemeConfigBean("背景颜色", BACKGROUND_COLOR, "#FFFFFFFF"),
            TEXT_COLOR to ThemeConfigBean("文字颜色", TEXT_COLOR, "#FF000000"),
        )
    )
    //endregion

    //region 淡绿主题
    private fun greenTheme() = ThemeWithValueResult(
        themeBean = ThemeBean(name = "淡绿主题", description = "", isUsed = false),
        themeConfigBeanList = mutableMapOf(
            BACKGROUND_COLOR to ThemeConfigBean("背景颜色", BACKGROUND_COLOR, "#D7E3CD"),
            TEXT_COLOR to ThemeConfigBean("文字颜色", TEXT_COLOR, "#FF000000"),
        )
    )
    //endregion

    //region 淡黄主题
    private fun yellowTheme() = ThemeWithValueResult(
        themeBean = ThemeBean(name = "淡黄主题", description = "", isUsed = false),
        themeConfigBeanList = mutableMapOf(
            BACKGROUND_COLOR to ThemeConfigBean("背景颜色", BACKGROUND_COLOR, "#FFFFCC"),
            TEXT_COLOR to ThemeConfigBean("文字颜色", TEXT_COLOR, "#FF000000"),
        )
    )
    //endregion

    //region 淡蓝主题
    private fun blueTheme() = ThemeWithValueResult(
        themeBean = ThemeBean(name = "淡蓝主题", description = "", isUsed = false),
        themeConfigBeanList = mutableMapOf(
            BACKGROUND_COLOR to ThemeConfigBean("背景颜色", BACKGROUND_COLOR, "#CCE5FF"),
            TEXT_COLOR to ThemeConfigBean("文字颜色", TEXT_COLOR, "#FF000000"),
        )
    )
    //endregion

    //region 黑色主题
    private fun darkTheme() = ThemeWithValueResult(
        themeBean = ThemeBean(name = "黑色主题", description = "", isUsed = false),
        themeConfigBeanList = mutableMapOf(
            BACKGROUND_COLOR to ThemeConfigBean("背景颜色", BACKGROUND_COLOR, "#151515"),
            TEXT_COLOR to ThemeConfigBean("文字颜色", TEXT_COLOR, colorString(174)),
        )
    )
    //endregion
}

private fun changeColorRGB(color: Int, dr: Int, dg: Int, db: Int): Int {
    val lr = Color.red(color) + dr
    val lg = Color.green(color) + dg
    val lb = Color.blue(color) + db
    return Color.rgb(lr.coerceIn(0, 255), lg.coerceIn(0, 255), lb.coerceIn(0, 255))
}

private fun colorString(rgb: Int) = String.format("#%08X", Color.rgb(rgb, rgb, rgb))
private fun View.isLighten() = this.tag != null && this.tag.toString().contains(COLOR_LIGHTEN)
private fun View.isNoTheme() = this.tag != null && this.tag.toString().contains(NO_THEME)
private fun View.isDeepen() = this.tag != null && this.tag.toString().contains(COLOR_DEEPEN)
private fun View.isSameColorToStroke() = this.tag != null && this.tag.toString().contains(SAME_COLOR_TO_STROKE)
private fun Int.isDarkColor() = Color.luminance(this) < 0.5

/**
 * 颜色转为十六进制
 */
private fun Int.toColorString() = String.format("#%08X", this)

/**
 * 两个颜色的对比度
 */
private fun Int.contrast(toColor: Int) = ColorUtils.calculateContrast(this, toColor)

class ThemeWithValueResult(
    var themeBean: ThemeBean,
    var themeConfigBeanList: Map<String, ThemeConfigBean>
)


